/*
1. Write a program that calls fork(). Before calling fork(), have the main
process access a variable (e.g., x) and set its value to something (e.g., 100).
What value is the variable in the child process?
What happens to the variable when both the child and parent change the value of x?

Answer:
It has separate stack and heap and follow strategy COW which
keep using parent pages

2. Write a program that opens a file (with the open() system call) and then calls
fork() to create a new process. Can both the child and parent access the
file descriptor returned by open()? What happens when they are writing to
the file concurrently, i.e., at the same time?

Answer:
Since Kernel 3.14 this problem was fixed they can access now concurrently

3. Write another program using fork(). The child process should print “hello”;
the parent process should print “goodbye”. You should try to ensure that the
 child process always prints first; can you do this without calling wait() in the parent?

Answer:
probably no

4. Write a program that calls fork() and then calls some form of exec()
to run the program /bin/ls. See if you can try all of the variants of exec(),
including (on Linux) execl(), execle(), execlp(), execv(), execvp(), and execvpe().
Why do you think there are so many variants of the same basic call?

Answer:
who knows :P

5. Now write a program that uses wait() to wait for the child process
to finish in the parent. What does wait() return? What happens if you use wait() in the child?

Answer:
wait for the child process -> no child processes error

6. Write a slight modification of the previous program, this time using
waitpid() instead of wait(). When would waitpid() be useful?

Answer:
When we want to control which child state changes we are waiting for

7. Write a program that creates a child process, and then in the child
closes standard output (STDOUT_FILENO). What happens if the child calls
printf() to print some output after closing the descriptor?

Answer:
Simply it will not be printed

8. Write a program that creates two children, and connects the standard
output of one to the standard input of the other, using the pipe() system call.
 */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static int x_global = 10;
static const char *filename = "dummy";
static const char *dummy_text = "hello baby";

void program_8(void)
{
  int pipefd[2];
  if (pipe(pipefd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  int rc;
  if ((rc = fork()) == 0) {
    close(pipefd[1]); /* close write */
    puts("child 1");
    char buffer[10];
    ssize_t bytes;
    while ((bytes = read(pipefd[0], buffer, 10)) > 0) {
      buffer[bytes] = '\0';
      printf("buffer: %s\n", buffer);
    }
    exit(EXIT_SUCCESS);
  }

  if ((rc = fork()) == 0) {
    puts("child 2");
    close(pipefd[0]); /* close read */
    write(pipefd[1], dummy_text, strlen(dummy_text));
    exit(EXIT_SUCCESS);
  }

  close(pipefd[0]);
  close(pipefd[1]);
  wait(NULL);
}

void program_7(void)
{
  int rc = fork();
  if (rc == 0) {
    puts("child before closing");
    close(STDOUT_FILENO);
    puts("child after closing");
  } else if (rc > 0) {
    wait(NULL);
  } else {
    perror("fork failed\n");
  }
}

void program_5(void)
{
  int rc = fork();
  if (rc == 0) {
    printf("hello\n");
    pid_t pid;
    if ((pid = waitpid(-1, NULL, WNOHANG)) == -1) {
      perror("wait failed");
    }
  } else if (rc > 0) {
    //    wait(NULL);
    waitpid(-1, NULL, 0);
    printf("goodbye\n");
  } else {
    perror("fork failed\n");
  }
}

void program_4(void)
{
  int rc = fork();
  if (rc == 0) {
    char **iter = environ;
    puts("print environment");
    while (*iter) {
      printf("%s\n", *iter);
      ++iter;
    }

    const char *envp[] = { "PATH=/bin:/usr/bin", NULL };
    // execl("/bin/ls", "ls", (char *)NULL);
    /* execute and search in PATH */
    // execlp("ls", "ls", (char *)NULL);
    /* specify environment variable of executed
       program but not change the actual PATH */
    /* execle("/bin/ls", "ls", (char *)NULL, envp); */
    /* execv use list instead of variadic parameter */
    execv("/bin/ls", (char*[]){"ls", NULL});
  } else if (rc > 0) {
    wait(NULL);
    printf("goodbye\n");
  } else {
    perror("fork failed\n");
  }
}

void program_3(void)
{
  int rc = fork();
  if (rc == 0) {
    printf("hello\n");
  } else if (rc > 0) {
    wait(NULL);
    printf("goodbye\n");
  } else {
    perror("fork failed\n");
  }
}

void program_2(void)
{
 int fd;
 if ((fd = open(filename,
                O_CREAT | O_TRUNC | O_WRONLY,
                S_IRUSR | S_IWUSR)) == -1) {
   perror("error occured");
   return;
 }

 int rc = fork();
 if (rc == 0) {
   write(fd, dummy_text, strlen(dummy_text));
 } else if (rc > 0) {
   write(fd, dummy_text, strlen(dummy_text));
   wait(NULL);
 } else {
   fprintf(stderr, "fork failed\n");
 }

 close(fd);
}

void program_1(void)
{
  int x_local = 10;
  int rc = fork();
  if (rc == 0) {
    puts("In child process");
    printf("child x_local: %d\n", x_local);
    printf("parent x_global: %d\n", x_global);
  } else if (rc > 0) {
    x_local = 5;
    x_global = 5;
    printf("parent x_local: %d\n", x_local);
    printf("parent x_global: %d\n", x_global);
    puts("In parent process");
    printf("the child process %d was created\n", rc);

    int wstatus;
    rc = wait(&wstatus);
    printf("the child %d finished its work status: %d\n", rc, wstatus);
  } else {
    fprintf(stderr, "fork failed\n");
  }
}

int main(void)
{
  program_8();
  return 0;
}
